﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Resources;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Collections.ObjectModel;
using System.Web;
using KPIS.GERP.Models.ICM;
using System.Data;
using System.ComponentModel;

namespace KPIS.GERP.Models.Class
{
    /// <summary>
    /// Model utility, basic code that frequenctly uses
    /// </summary>
    public static class Utility
    {
        /// <summary>
        /// Use as automatic generate LINQ query where, every not default property in param object will be include in query where result
        /// </summary>
        /// <param name="filter">Model to filter, 
        /// set value which properties you want to make it as criteria, 
        /// don't set value on properties you don't want to make it as criteria.
        /// In case string type properties in model, use ! as start charactor to tell methods as not equal value of the rest charactor
        /// ! = not equal,  less than,
        ///  more than,
        /// less than and equal,
        /// more than and equal.</param>
        /// <param name="param">output to insert as params array in where expression
        /// </param>
        /// <returns>linq query string</returns>
        public static string GenerateWhereString(object filter, out object[] param)
        {
            var strFilterList = new List<string>();
            var listParam = new List<object>();
            int paramIndex = 0;
            foreach (var property in filter.GetType().GetProperties())
            {
                object obj = property.GetValue(filter, null);
                bool bEqual = true;

                if (IsNullOrDefaultValue(obj)) continue;

                if (obj is string)
                {
                    obj = obj.ToString().Replace("\"", "\"\"");
                    string strPrefix = (obj as string).Substring(0, 1).Trim();
                    bEqual = !(obj as string).StartsWith("!") && !(obj as string).StartsWith("<") && !(obj as string).StartsWith(">");
                    if (!bEqual) obj = (obj as string).Substring(1, (obj as string).Length - 1);
                    strFilterList.Add(property.Name + (bEqual ? "=" : (strPrefix == "!" ? "!=" : strPrefix)) + "\"" + (obj as string) + "\"");
                }
                var eachFilter = property.Name + (bEqual ? "=" : "!=") + "@" + (paramIndex++);
                strFilterList.Add(eachFilter);
                listParam.Add(obj);
            }

            param = listParam.ToArray();
            var result = string.Join(" And ", strFilterList);
            return result;
        }

        /// <summary>
        /// Determine any type object if it's default value or not?
        /// </summary>
        /// <param name="obj">Object to check</param>
        /// <returns>Result check</returns>
        public static bool IsNullOrDefaultValue(object obj)
        {
            if (obj == null) return true;
            Type t = obj.GetType();
            if (t == typeof(string) && string.IsNullOrWhiteSpace(obj as string)) return true;
            if (!string.IsNullOrWhiteSpace(t.FullName) && !t.FullName.StartsWith("System")) return true;
            if (t.IsArray) return true;
            if (IsNumericType(obj) && (obj.ToString() == "0" || obj.ToString() == "-1")) return true;
            if (t == typeof(DateTime) && (DateTime)obj == DateTime.MinValue) return true;
            return false;
        }

        /// <summary>
        /// Determine any type object if it's any type of numeric or not?
        /// </summary>
        /// <param name="o">Object to check</param>
        /// <returns>Result check</returns>
        public static bool IsNumericType(object o)
        {
            switch (Type.GetTypeCode(o.GetType()))
            {
                case TypeCode.Byte:
                case TypeCode.SByte:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.Decimal:
                case TypeCode.Double:
                case TypeCode.Single:
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Get display name match to type of look up data, default is enum option of request param
        /// </summary>
        /// <param name="lookUpType">enum look up type</param>
        /// <returns>string display name</returns>
        public static string LookUpTypeNameCaption(IcmMasLookUpType.LookUpType lookUpType)
        {
            try
            {
                var rm = new ResourceManager("KPIS.GERP.Models.Resources.IcmLookUpTypeCaption", Assembly.GetExecutingAssembly());
                return rm.GetString(lookUpType.ToString());
            }
            catch (Exception ex)
            {
                GerpLog.Error(string.Format("{0}\rCan't load from resource file\rRemark: LookUpTypeString = {1}, LookUpTypeId = {2}", ex.Message, lookUpType.ToString(), lookUpType));
                try
                {
                    return lookUpType.ToString();
                }
                catch (Exception ex2)
                {
                    GerpLog.Error(ex2.Message);
                }
            }
            return lookUpType.ToString();
        }

        public static string Base64Decode(string data)
        {
            try
            {
                System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
                System.Text.Decoder utf8Decode = encoder.GetDecoder();

                byte[] todecode_byte = Convert.FromBase64String(data);
                int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
                char[] decoded_char = new char[charCount];
                utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
                string result = new String(decoded_char);
                return result;
            }
            catch (Exception e)
            {
                throw new Exception("Error in base64Decode" + e.Message);
            }
        }

        public static int BuddishYear { get { return new ThaiBuddhistCalendar().GetYear(DateTime.Now); } }

        public static DataTable ToDataTable<T>(this IList<T> data)
        {
            PropertyDescriptorCollection props =
                TypeDescriptor.GetProperties(typeof(T));
            DataTable table = new DataTable();
            for (int i = 0; i < props.Count; i++)
            {
                PropertyDescriptor prop = props[i];
                table.Columns.Add(prop.Name, prop.PropertyType);
            }
            object[] values = new object[props.Count];
            foreach (T item in data)
            {
                for (int i = 0; i < values.Length; i++)
                {
                    values[i] = props[i].GetValue(item);
                }
                table.Rows.Add(values);
            }
            return table;
        }

        public static DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
        {
            DataTable dtReturn = new DataTable();

            // column names 
            PropertyInfo[] oProps = null;

            if (varlist == null) return dtReturn;

            foreach (T rec in varlist)
            {
                // Use reflection to get property names, to create table, Only first time, others will follow 
                if (oProps == null)
                {
                    oProps = ((Type)rec.GetType()).GetProperties();
                    foreach (PropertyInfo pi in oProps)
                    {
                        Type colType = pi.PropertyType;

                        if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition()      
                            ==typeof(Nullable<>)))
                        {
                            colType = colType.GetGenericArguments()[0];
                        }

                        dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
                    }
                }

                DataRow dr = dtReturn.NewRow();

                foreach (PropertyInfo pi in oProps)
                {
                    dr[pi.Name] = pi.GetValue(rec, null) == null ?DBNull.Value :pi.GetValue(rec,null);
                }

                dtReturn.Rows.Add(dr);
            }

            return dtReturn;
        }

    }

    public class ElmahErrorHandler : IErrorHandler
    {
        #region IErrorHandler Members

        public bool HandleError(Exception error)
        {
            return false;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            if (error == null)
            {
                return;
            }
            if (HttpContext.Current == null)
            {
                return;
            }
            Elmah.ErrorSignal.FromCurrentContext().Raise(error);
        }

        #endregion
    }

    /// <summary>
    /// Use this attribute to every service in WCF, if uncaught error occur, it'll record error log
    /// </summary>
    public class ServiceErrorBehaviorAttribute : Attribute, IServiceBehavior
    {
        private readonly Type errorHandlerType;

        public ServiceErrorBehaviorAttribute(Type errorHandlerType)
        {
            this.errorHandlerType = errorHandlerType;
        }

        #region IServiceBehavior Members

        public void AddBindingParameters(ServiceDescription serviceDescription,
        ServiceHostBase serviceHostBase,
        Collection<ServiceEndpoint> endpoints,
        BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
        ServiceHostBase serviceHostBase)
        {
            IErrorHandler errorHandler;
            errorHandler = Activator.CreateInstance(errorHandlerType) as IErrorHandler;
            if (errorHandler != null)
            {
                foreach (ChannelDispatcherBase dispatcher in serviceHostBase.ChannelDispatchers)
                {
                    ChannelDispatcher cd = dispatcher as ChannelDispatcher;
                    cd.ErrorHandlers.Add(errorHandler);
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription,
        ServiceHostBase serviceHostBase)
        {
        }

        #endregion
    }

}
